package com.aliyun.messagemgt.application.service.Impl;


import com.aliyun.messagemgt.application.service.MediaUrlHandler;
import com.aliyun.messagemgt.application.service.media.BloomFilter;
import com.aliyun.messagemgt.application.service.utils.MediaTypeUtil;
import com.aliyun.messagemgt.common.dto.send.MsgBody;
import com.aliyun.messagemgt.common.enums.MediaLimitEnum;
import com.aliyun.messagemgt.util.OSSUtil;
import com.aliyun.messagemgt.common.constants.push.MediaHandlerCodeConstant;
import com.aliyun.messagemgt.common.errorcode.MediaErrorCode;
import com.aliyun.messagemgt.common.exceptions.BaseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

/**
 * Created with IDEA
 *
 * @author:zhangxingxing 0027012120
 * @date:2019/1/31
 * @time:10:58
 */
@Component
public class MediaUrlHandlerImpl implements MediaUrlHandler {

    private static final Logger logger = LoggerFactory.getLogger(MediaUrlHandlerImpl.class);
    @Autowired
    private BloomFilter bloomFilter;
    /**
     * 类常量
     */
    private String https = "https";
    private String ftp = "ftp";
    private String get = "GET";
    private String application = "application";
    private static final String SSL_STR = "SSL";
    /**
     * 1. 布隆过滤器去重比较
     * (1) 判断为重复 则到数据库获取对应的url，获取不到则按 (2) 处理
     * (2) 判断不存在，则 根据url取流 => 判断大小(超限不处理，返回响应错误码) => 存到OSS服务器返回相应的url
     * => 将对应结果存到数据库
     *
     * @param url
     * @return
     */
    @Override
    public Integer urlHandle(MsgBody url) {
        // 布隆过滤器判断是否存在，不存在则加入
        boolean urlExsit = bloomFilter.containAndAdd(url.getContent());
        if (urlExsit) {
            Integer res = existHandle(url);
            // 如果有返回值则直接返回，否则继续执行 布隆过滤器不存在的逻辑
            if (MediaHandlerCodeConstant.SUCCESS.equals(res)) {
                return MediaHandlerCodeConstant.SUCCESS;
            }
        }
        return notExistHandle(url);
    }

    /**
     * 存在情况的处理 => 取数据库获取处理的记录
     *
     * @param url
     * @return
     */
    private Integer existHandle(MsgBody url) {
        /*UrlMgt urlMgt = urlMgtService.findUrlInfoByOriginUrl(url.getContent());
        if (urlMgt == null) {
            return MediaHandlerCodeConstant.NO_URL;
        }
        url.setContent(urlMgt.getUrlCode());*/
        return MediaHandlerCodeConstant.SUCCESS;
    }

    /**
     * 不存在情况的处理 => 取数据库获取处理的记录
     * 根据url取流 => 判断大小 => 存到OSS服务器
     *
     * @param msgBody
     * @return PushResultEnum 的错误码
     */
    private Integer notExistHandle(MsgBody msgBody) {
        Integer res;
        String uploadFileType = null;
        String originUrl = msgBody.getContent();
        InputStream inputStream = null;
        try {
            // HTTPS
            if (originUrl.startsWith(https)) {
                URL requestUrl = new URL(originUrl);
                HttpURLConnection connection = (HttpURLConnection) requestUrl.openConnection();
                logger.info("connection {}",connection);
                trustAllHttpsCertificates();
                HostnameVerifier hv = new HostnameVerifierImpl();
                HttpsURLConnection.setDefaultHostnameVerifier(hv);
                // 创建链接
                connection = (HttpsURLConnection) requestUrl.openConnection();
                connection.setRequestMethod(get);
                connection.setConnectTimeout(10000);
                inputStream = connection.getInputStream();
                uploadFileType = getUploadFileType(uploadFileType,msgBody,connection);
            }
            // Ftp
            else if (originUrl.startsWith(ftp)) {
                URL requestUrl = new URL(originUrl);
                URLConnection connection = requestUrl.openConnection();
                inputStream = connection.getInputStream();
                String fileDir = requestUrl.getFile();
                int dotIndex = fileDir.indexOf('.');
                if (dotIndex != -1) {
                    uploadFileType = fileDir.substring(dotIndex + 1);
                }
                else if (dotIndex == -1 && MediaLimitEnum.IMAGE.name().equalsIgnoreCase(msgBody.getType())){
                    uploadFileType = "png";
                }
                else {
                    throw new BaseException(MediaErrorCode.INVALID_TYPE_ERROR);
                }
            }
            // Http
            else {
                URL requestUrl = new URL(originUrl);
                HttpURLConnection connection = (HttpURLConnection) requestUrl.openConnection();
                inputStream = connection.getInputStream();
                uploadFileType = getUploadFileType(uploadFileType,msgBody,connection);
            }
        } catch (Exception e) {
            logger.error("获取流失败：{}", e.toString());
            res = MediaHandlerCodeConstant.URL_IO_ERROR;
            return res;
        }
        // 根据url流获取 media 的 size
        byte[] data = readInputStream(inputStream);
        if (data.length > MediaLimitEnum.valueOf(msgBody.getType()).getLimitSize()) {
            res = MediaHandlerCodeConstant.DATA_OVER_LIMIT;
            return res;
        }
        logger.info("size: {}", data.length);
        // 根据byte新建流，因为流读完了点位会改变
        InputStream newInputStream = new ByteArrayInputStream(data);
        // 流传到oss
        if (uploadFileType == null) {
            uploadFileType = "png";
        }
        String transferUrl = null;
        String uuid = UUID.randomUUID().toString();
        try {
            transferUrl = OSSUtil.uploadInputStream(newInputStream, uploadFileType, uuid);
        } catch (Exception e) {
            logger.error("上传OSS失败：{}", e.toString());
            res = MediaHandlerCodeConstant.URL_IO_ERROR;
            return res;
        }
        try {
            newInputStream.close();
        } catch (Exception e) {
            logger.error("关闭流失败：{}", e.toString());
            res = MediaHandlerCodeConstant.URL_IO_ERROR;
            return res;
        }
        // content变成UUID
        msgBody.setContent(uuid);
        /*UrlMgt urlMgt = new UrlMgt().setOriginUrl(originUrl)
                .setOriginUrlHash(originUrl.hashCode())
                .setTransferUrl(transferUrl)
                .setUrlCode(uuid);*/
        //urlMgtService.batchUrlMgtInsert(Arrays.asList(urlMgt));
        res = MediaHandlerCodeConstant.SUCCESS;
        return res;
    }

    private String getUploadFileType(String uploadFileType,MsgBody msgBody, HttpURLConnection connection){
        String type = uploadFileType;
        // 获取类型，application开头或者null才考虑guessContentTypeFromName，如果都判断不了，默认为png
        type = MediaTypeUtil.getType(msgBody, connection.getContentType());
        if (type == null || type.startsWith(application)) {
            type = HttpURLConnection.guessContentTypeFromName(msgBody.getContent());
            type = MediaTypeUtil.getType(msgBody, type);
        }
        if (MediaLimitEnum.IMAGE.name().equalsIgnoreCase(msgBody.getType())
                && type == null) {
            type = "png";
        }
        else if (!MediaLimitEnum.IMAGE.name().equalsIgnoreCase(msgBody.getType())
                && type == null) {
            throw new BaseException(MediaErrorCode.INVALID_TYPE_ERROR);
        }
        return type;
    }

    /**
     * 将流转换为字节
     *
     * @param is 通过url获取到的流
     * @return data 转成byte的流
     */
    public byte[] readInputStream(InputStream is) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int length = -1;
        try {
            while ((length = is.read(buffer)) != -1) {
                byteArrayOutputStream.write(buffer, 0, length);
            }
            byteArrayOutputStream.flush();
        } catch (IOException e) {
            logger.error("Error to read response: ", e);
        }

        byte[] data = byteArrayOutputStream.toByteArray();

        try {
            is.close();
            byteArrayOutputStream.close();
        } catch (IOException e) {
            logger.error("Error to close the steam: ", e);
        }

        return data;
    }

    public static void trustAllHttpsCertificates() throws NoSuchAlgorithmException {
        javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];
        javax.net.ssl.TrustManager tm = new AllTrustManager();
        trustAllCerts[0] = tm;
        javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext.getInstance(SSL_STR);
        try {
            sc.init(null, trustAllCerts, null);
        } catch (KeyManagementException e) {
            logger.error("KeyManagementException",e);

        }
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    }

    static class AllTrustManager implements javax.net.ssl.TrustManager, javax.net.ssl.X509TrustManager {
        @Override
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        public boolean isServerTrusted(java.security.cert.X509Certificate[] certs) {
            return true;
        }

        public boolean isClientTrusted(java.security.cert.X509Certificate[] certs) {
            return true;
        }

        @Override
        public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
                throws java.security.cert.CertificateException {
            return;
        }

        @Override
        public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
                throws java.security.cert.CertificateException {
            return;
        }
    }
}
